/* 
 * File:   SelectTable.cpp
 * Author: vikrantsingh
 * 
 * Created on 3 November, 2013, 12:32 AM
 */

#include <iomanip>
#include <algorithm>
#include "SelectTable.h"
#include "Validation.h"
#include "../Sysfiles/SysCatalog.h"
#include "TableScan.h"
#include "PostFixEvaluator.h"

#include "../dependency/ExternDefOfGlobalVariables.h"
#include "../IndexManager/BPlusTree.h"
#include "../dependency/util.h"
#include "../IndexManager/BPlusTree.h"
#include "../IndexManager/IndexHeaderPage.h"
#include "../IndexManager/IndexUtil.h"

SelectTable::SelectTable() {
    _isTableScanOpen = false;
    _allColumns = false;
    _totalNumOfRecords = 0;
    _recordDisplayed = 0;
    resultFromIndex = false;
}

SelectTable::SelectTable(const SelectTable& orig) {
}

SelectTable::~SelectTable() {
    if (_isTableScanOpen == true) {
        delete _heapFile;
        delete _tableScan;
    }
}

STATUS_CODE SelectTable::run(QueryStructures *queryStruct) {
    DEBUG("\n\n ******IN SELECT TABLE\n\n");
    Timer time;
    time.startTimer();
    int err = fetchResultSet(queryStruct);
    time.endTimer();

    //time.displayElapsedTimeInMilliSeconds();
    if (err != SUCCESS) return err;
    //return err;
    //while()
    int noRec = displayResultSet(queryStruct);
    if(noRec <  ROWS_PER_PAGE){
        return SUCCESS;
    }
        
    while (true) {
        cout << "\n--more--:";
        char choice;
        //string choice;

        cin >> choice;
        fflush(stdout);
        //fflush(stdin);
        // cin>>choice;
        //choice = getchar();
        if (choice != 'E' && choice != 'e') {
            time.startTimer();
            int rowCount = loadNextPage();
            time.endTimer();
            if (rowCount == 0) {
                break;
            } else
                displayResultSet(queryStruct);
        } else
            break;

    }
    //cout << "\nFINISHED" << resultSet.size();


    return SUCCESS;
}

int SelectTable::displayResultSet(QueryStructures *queryStruct) {
    if (_allColumns == false) {
        for (unsigned int i = 0; i < resultSetColumnList_.size(); i++) {
            cout << setiosflags(ios::left) << setw(20) << resultSetColumnList_[i] << " ";
        }
    } else {
        for (int i = 0; i < _schema.numOfColumns; i++) {
            cout << setiosflags(ios::left) << setw(20) << _schema.columnNames[i] << " ";
        }
    }
    cout << endl;



    DEBUG("\n\n ******result set size : " << resultSet.size());
    //    for (unsigned i = 0; i < resultSet.size(); i++) {
    //    cout << "total num rec" << totalNumOfRecords << endl;
    for (unsigned i = 0; i < resultSet.size(); i++) {
        Tuple tuple = resultSet[i];

        if (_allColumns == false) {
            for (unsigned int j = 0; j < resultSetColumnList_.size(); j++) {
                std::string data = tuple.getFeildInString(resultSetColumnList_[j]);
                cout << setiosflags(ios::left) << setw(20) << data << " ";

            }
        } else {
            for (int j = 0; j < _schema.numOfColumns; j++) {
                std::string data = tuple.getFeildInString(_schema.columnNames[j]);
                cout << setiosflags(ios::left) << setw(20) << data << " ";
            }
        }

        cout << endl;
//        tuple.free();//////free data
    }
    //    resultSet_str.clear();
    resetiosflags(ios::left);
    cout<<"\n#No of Records Displayed : "<<resultSet.size()<<endl;
    return resultSet.size();

}

STATUS_CODE SelectTable::fetchResultSet(QueryStructures *queryStruct) {
    _totalNumOfRecords = 0;
    time(&_start);
    _timer.startTimer();
    resultSetColumnList_ = queryStruct->resultSetColumnList;
    _allColumns = queryStruct->allColumns;
    whereConditionExprList_.assign(
            queryStruct->whereConditionExprList.begin(),
            queryStruct->whereConditionExprList.end());
    DEBUG("inside selectTable execute");
    int error = Validation::isValid();
    if (SUCCESS != error) {
        return error;
    }
    SysCatalog cat;

    error = cat.getSchema(queryStruct->tableName.c_str(), _schema);
    if (error == TABLE_NOT_FOUND) {
        return error;
    }


    int tableId = 0;
    cat.getTableId(queryStruct->tableName.c_str(), tableId);

    _heapFile = new Heap(_schema.tableName.c_str());
    
    resultFromIndex = false;
    _currentRidIndexSearch=0;
    _ridList.clear();
    bool indexExists = false;
    bool isORContains = false;
    int indexHeaderPageNum = -1;
    vector<int> keyColNo;
    //std::vector<int> indexHeaderPageNumberList;
    vector<string> indexedColumn;

    for (unsigned i = 0; i < whereConditionExprList_.size(); ++i) {
        if (whereConditionExprList_[i].type_ == WhereClause::OPERATOR_TYPE) {
            if (strcmp(whereConditionExprList_[i].operatorValue.c_str(), "OR") == 0) {
                isORContains = true;
                break;
            }
        }
        if (whereConditionExprList_[i].type_ == WhereClause::IDENTIFIER_TYPE) {
            indexedColumn.push_back(whereConditionExprList_[i].identifierValue);
        }
    }

    if (!isORContains && whereConditionExprList_.size() > 0) {
        indexExists = cat.getBestIndexIfExist(_schema, indexedColumn, indexHeaderPageNum, tableId, keyColNo);
    }

    // check is index exists
    /*
 PICK BEST INDEX
     *    ITERATE ALL INDEX
     *      CHECK IF ALL KEY ATTRIBUTE ARE IN WHERE
     *      IF NO WE CAN'T USE INDEX
     *      IF YES CHECK WHO MANY FROM WHERE CLAUSE SELECT MAX ONE

     */

//    cout << "\n is Index Exists :" << indexExists << endl;
    if (indexExists == true && USING_INDEX == true && isORContains == false && indexHeaderPageNum != -1) {
        
//        cout << "\n Index header Page no : " << indexHeaderPageNum << endl;

        IndexHeaderPage indexHeaderPage(indexHeaderPageNum);
        //        cout << "in index check1" << endl;
        BPlusTree bplusTree(indexHeaderPageNum);
        //        cout << "in index check1" << endl;
        int keySize = indexHeaderPage.getKeySize();


        vector<string> columnList;
        vector<string> literalList;
        vector<int> operatorList;
        int operatorType;
        for (unsigned j = 0; j < keyColNo.size(); j++) {
//            cout<<" For key Col No"<<keyColNo[j]<<endl;
            for (unsigned i = 0; i < whereConditionExprList_.size(); i++) {

                if (whereConditionExprList_[i].type_ == WhereClause::IDENTIFIER_TYPE) {
                    if (_schema.getColumnNum(whereConditionExprList_[i].identifierValue.c_str()) == keyColNo[j]) {
                        ///////colName
                        columnList.push_back(whereConditionExprList_[i].identifierValue);
                        //////Literal
                        if (whereConditionExprList_[i + 1].type_
                                == WhereClause::LITERAL_TYPE) {
                            literalList.push_back(whereConditionExprList_[i + 1].literalValue);
                        }
                        ///////operator
                        if (whereConditionExprList_[i + 2].type_
                                == WhereClause::OPERATOR_TYPE) {
                            if (whereConditionExprList_[i + 2].operatorValue == "=") {
                                int op=EQUAL_TO;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == "<") {
                                int op=LESS_THAN;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == ">") {
                                int op=GREATER_THAN;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == "<=") {
                                int op=LESS_THAN_OR_EQUAL_TO;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == ">=") {
                                int op=GREATER_THAN_OR_EQUAL_TO;
                                operatorList.push_back(op);
                            }
                        }
                    }

                }
            }
        }
//        cout<<"\n column list:";
//        for(unsigned k=0;k<columnList.size();k++){
//            cout<<" "<<columnList[k];
//        }
//        cout<<"\n operator list:";
//        for(unsigned k=0;k<operatorList.size();k++){
//            cout<<" "<<operatorList[k];
//        }
//        cout<<"\n literal list:";
//        for(unsigned k=0;k<literalList.size();k++){
//            cout<<" "<<literalList[k];
//        }
//        cout<<"\n";
        char *keyData = new char[keySize];
        int offset = 0;
        for (unsigned i = 0; i < columnList.size(); i++) {
            int fieldNum = _schema.getColumnNum(columnList[i]);
            if (_schema.fieldTypes.at(fieldNum) == TYPE_INTEGER) {
                int recordData = Util::string_to_int(literalList[i]);
                DEBUG("keyData" << recordData);
                memcpy(&keyData[offset], &recordData,
                        _schema.fieldLengths[fieldNum]);
                offset = offset + _schema.fieldLengths[fieldNum];
            } else if (_schema.fieldTypes.at(fieldNum) == TYPE_FLOAT) {
                float recordData = Util::string_to_float(literalList[i]);
                DEBUG("keyData" << recordData);
                memcpy(&keyData[offset], &recordData,
                        _schema.fieldLengths[fieldNum]);
                offset = offset + _schema.fieldLengths[fieldNum];
            } else if (_schema.fieldTypes.at(fieldNum) == TYPE_VARCHAR) {
                const char *recordData = literalList[i].c_str();
                memcpy(&keyData[offset], recordData,
                        _schema.fieldLengths[fieldNum]);
                offset = offset + _schema.fieldLengths[fieldNum];
            }
        }
        //        cout << "in index check4" << endl;
        //bplusTree.search(keyData, _ridList);
//        cout<<"\n keydata:"<<IndexUtil::keyToString(keyData,&indexHeaderPage)<<endl;
        bplusTree.search(keyData,operatorList, _ridList);
        resultFromIndex = true;
        delete keyData;
        //        if (USE_CLIENT_SIDE_SORT == true) {
        //            sort(_ridList.begin(), _ridList.end());
        //        }
        int count = 0;
        //        cout<<"\nRID LIST SIZE"<< _ridList.size()<<endl;
        for (unsigned j = 0; j < _ridList.size(); j++) {
            char *record;
            unsigned recordLen;
            _heapFile->getRecord(_ridList[j], recordLen, record);                     
            Tuple tuple(_schema, record, recordLen);
            _currentRidIndexSearch = j + 1;
            if (queryStruct->whereConditionExprList.size() > 0) {

                PostFixEvaluator postFixEvaluator(tuple);
                
                if (postFixEvaluator.evaluate(queryStruct->whereConditionExprList) == true) {
                    //add to result set;
                    DEBUG("tuple" << tuple.toString());
//                     cout<<"\ntuple " << tuple.toString();
                    resultSet.push_back(tuple);                    
                    count++;
                    if (count == ROWS_PER_PAGE) {
                        break;
                    }
                }

            }


        }
        //        cout << "in index check6" << endl;

        return SUCCESS;
    }

    _tableScan = new TableScan(_heapFile);
    _isTableScanOpen = true;
    char *record;
    unsigned recordLen;
    int count = 0;
    //    resultSet.clear();

    while (_tableScan->getNextRec(record, recordLen) != SCAN_OVER) {
        //        cout << "\nreclen1=" << recordLen;

        Tuple tuple(_schema, record, recordLen);
        //        cout << "\nreclen2=" << recordLen;
        if (queryStruct->whereConditionExprList.size() > 0) {

            PostFixEvaluator postFixEvaluator(tuple);
            if (postFixEvaluator.evaluate(queryStruct->whereConditionExprList) == true) {
                //add to result set;
                DEBUG("tuple" << tuple.toString());
                resultSet.push_back(tuple);
                count++;
            }

        } else {//no where conditions
            //add all to resultset
            DEBUG("tuple" << tuple.toString());
            //cout << "\ntuple" << tuple.toString();
            resultSet.push_back(tuple);
            count++;
        }

        _totalNumOfRecords = count;
        if (count == ROWS_PER_PAGE) {
            break;
        }
    }
    return SUCCESS;

}

STATUS_CODE SelectTable::fetchResultSetAll_WithoutIndex(QueryStructures *queryStruct) {
    _totalNumOfRecords = 0;
    time(&_start);
    _timer.startTimer();
    resultSetColumnList_ = queryStruct->resultSetColumnList;
    _allColumns = queryStruct->allColumns;
    whereConditionExprList_.assign(
            queryStruct->whereConditionExprList.begin(),
            queryStruct->whereConditionExprList.end());
    DEBUG("inside selectTable execute");
    int error = Validation::isValid();
    if (SUCCESS != error) {
        return error;
    }
    SysCatalog cat;

    error = cat.getSchema(queryStruct->tableName.c_str(), _schema);
    if (error == TABLE_NOT_FOUND) {
        return error;
    }
    _heapFile = new Heap(_schema.tableName.c_str());

    int tableId = 0;
    cat.getTableId(queryStruct->tableName.c_str(), tableId);

    bool indexExists = false;

    //    std::vector<int> indexHeaderPageNumberList;
    //    for (unsigned i = 0; i < whereConditionExprList_.size(); ++i) {
    //        if (whereConditionExprList_[i].type_ == WhereClause::IDENTIFIER_TYPE) {
    //            bool tmp = cat.isIndexExist(_schema, whereConditionExprList_[i].identifierValue, indexHeaderPageNumberList,tableId);
    //            if (indexExists == false && tmp == true) {
    //                indexExists = true;
    //            }
    //        }
    //    }

    // check is index exists
    //    cout<<"before index check"<<endl;
    if (indexExists == true && USING_INDEX == true) {
       //  */ 
    }

    _tableScan = new TableScan(_heapFile);
    _isTableScanOpen = true;
    char *record;
    unsigned recordLen;
    int count = 0;
    //    resultSet.clear();

    while (_tableScan->getNextRec(record, recordLen) != SCAN_OVER) {
        //        cout << "\nreclen1=" << recordLen;

        Tuple tuple(_schema, record, recordLen);
        //        cout << "\nreclen2=" << recordLen;
        if (queryStruct->whereConditionExprList.size() > 0) {

            PostFixEvaluator postFixEvaluator(tuple);
            if (postFixEvaluator.evaluate(queryStruct->whereConditionExprList) == true) {
                //add to result set;
                DEBUG("tuple" << tuple.toString());
                resultSet.push_back(tuple);
                count++;
            }

        } else {//no where conditions
            //add all to resultset
            DEBUG("tuple" << tuple.toString());
            //cout << "\ntuple" << tuple.toString();
            resultSet.push_back(tuple);
            count++;
        }

        _totalNumOfRecords = count;
        //        if (count == ROWS_PER_PAGE) {
        //            break;
        //        }
    }

    return SUCCESS;

}

int SelectTable::loadNextPage() {

    resultSet.clear();
    char *record;
    unsigned recordLen;
    int count = 0;
    //////////if in index ridlist
    if (resultFromIndex) {
        if (_ridList.size() > 0) {
            for (unsigned j = _currentRidIndexSearch; j < _ridList.size(); j++) {
                _currentRidIndexSearch = j + 1;
                _heapFile->getRecord(_ridList[j], recordLen, record);
                Tuple tuple(_schema, record, recordLen);

                if (queryStruct->whereConditionExprList.size() > 0) {

                    PostFixEvaluator postFixEvaluator(tuple);
                    if (postFixEvaluator.evaluate(queryStruct->whereConditionExprList) == true) {
                        //add to result set;
                        DEBUG("tuple" << tuple.toString());
                        resultSet.push_back(tuple);
                        
                        count++;
                        if (count == ROWS_PER_PAGE) {
                            break;
                        }
                    }

                }

            }
            

        }
        return count;
    }
    //////continue tableScan


    while (_tableScan->getNextRec(record, recordLen) != SCAN_OVER) {
        Tuple tuple(_schema, record, recordLen);
        if (whereConditionExprList_.size() > 0) {
            PostFixEvaluator postFixEvaluator(tuple);
            if (postFixEvaluator.evaluate(queryStruct->whereConditionExprList) == true) {
                //add to result set;
                DEBUG("tuple" << tuple.toString());
                resultSet.push_back(tuple);
                count++;
            }
        } else {//no where conditions
            //add all to resultset
            DEBUG("tuple" << tuple.toString());
            resultSet.push_back(tuple);
            count++;
        }

        if (count == ROWS_PER_PAGE) {
            break;
        }
    }
    _totalNumOfRecords = _totalNumOfRecords + count;
    //	timerUtil.updateNumberOfRows(totalNumOfRecords);
    //	DEBUG("count of rows="<<count);
    //	timerUtil.endTimer();
    //	std::string elapsedTime = timerUtil.getElapsedTimeInMilliSeconds();
    //	elapsedTime.append("  milliseconds");
    //	timerUtil.updateExecutionTimeMessage(elapsedTime);
    DEBUG("totalNumOfRecords" << _totalNumOfRecords);

    return count;

}
